package com.ee.bruscar.generator.model;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class TemplateClass
{
	private Class clazz;

	public TemplateClass(Class clazz)
	{
		this.clazz = clazz;
	}
	
	public Class getInnerClass()
	{
		return this.clazz;
	}
	
	public String getClassName()
	{
		return clazz.getName();
	}

	public String getSimpleName()
	{
		return clazz.getSimpleName();
	}
	
	public void getCustomClassesFromSetters(Map<String, TemplateClass> map)
	{
		
		if( !isCustomType())
		{
			return;
		}
		
		if( isList() )
		{
			return;
		}
		
		register( clazz, map );
		
		List<Method> setters = getRawSetters();
		
		for( Method setter : setters )
		{
			Class[] args = setter.getParameterTypes();
			if( args.length > 1 )
			{
				throw new RuntimeException(
						this.getClass().getName() 
						+  " only setters with a single argument are supported. "
						+ clazz.getName() + " // " + setter.getName() );
			}
			
			Class setterArgument = args[0];
			
			System.out.println( "Setter expects: " + setterArgument.getName() );
			
			TemplateClass tmpClass = new TemplateClass( setterArgument );
			
			//stop infinite recursion
			if( clazz.equals(setterArgument))
			{
				return;
			}
			
			if( tmpClass.isCustomType() && !tmpClass.isList() )
			{
				register( setterArgument, map );
				tmpClass.getCustomClassesFromSetters(map);
			}
			else if( tmpClass.isList() )
			{
				TemplateMethod setterMethod = new TemplateMethod( setter );
				TemplateClass listType = setterMethod.getSetterListType();
				
				//stop infinite recursion
				if( clazz.equals(listType.getInnerClass()))
				{
					return;
				}
				
				register( listType.getInnerClass(), map );
				
				listType.getCustomClassesFromSetters(map);
			}
			
		}
		
	}
	
	private boolean isList()
	{
		Class[] interfaces = clazz.getInterfaces();
		for( int i = 0 ; i < interfaces.length ; i++ )
		{
			if( interfaces[i].equals(List.class))
			{
				return true;
			}
		}
		return false;
	
	}
	
	private void register( Class clazz, Map<String, TemplateClass> map)
	{
		if( map.containsKey( clazz.getName() ))
		{
			return;
		}
		map.put( clazz.getName() , new TemplateClass( clazz ) );
	}
	
	public boolean isCustomType()
	{
		return !isPrimitive( clazz );
	}
	
	@SuppressWarnings("unchecked")
    public boolean isPrimitive( Class type )
    {
        if( type.getName().equals("boolean"))
        {
            return true;
        }
        if( type.getName().equals("int"))
        {
            return true;
        }
        if( type.getName().equals("long"))
        {
            return true;
        }
        if( type.getName().equals("double"))
        {
            return true;
        }
        if( type == java.lang.annotation.Annotation.class )
        {
            return true;
        }
        if( type == String.class )
        {
            return true;
        }
        if( type == Integer.class )
        {
            return true;
        }
        if( type == Boolean.class )
        {
            return true;
        }
        if( type == Date.class )
        {
            return true;
        }
        return false;
    }
	
	private List<Method> getRawSetters()
	{
		List<Method> settersList = new ArrayList<Method>();
		
		for( int i = 0 ; i < clazz.getMethods().length ; i++ )
		{
			Method method = clazz.getMethods()[i];
			if( method.getName().startsWith("set") && method.getName().length() > 3)
			{
				settersList.add( method );
			}
		}
		return settersList;
	}
	
	
	public List<TemplateMethod> getSetters() 
	{
		List<TemplateMethod> settersList = new ArrayList<TemplateMethod>();
		
		for( Method rawMethod : getRawSetters() )
		{
			settersList.add( new TemplateMethod( rawMethod ));
		}
		return settersList;
	}
	
	public void collectSetterListClasses( List<TemplateClass> collectingList, List<TemplateClass> classesSearched )
	{
		List<TemplateMethod> setters = getSetters();
		
		for( TemplateMethod setter : setters )
		{
			if( setter.isSetterList() )
			{
				
				if( !alreadyContains( setter.getSetterListType(), collectingList ))
				{
					collectingList.add( setter.getSetterListType() );
					setter.getSetterListType().collectSetterListClasses( collectingList, classesSearched );
				}
			}
			else if( setter.isCustom() )
			{
				if( !alreadyContains( setter.getSetterType(), classesSearched ) )
				{
					classesSearched.add( setter.getSetterType() );
					setter.getSetterType().collectSetterListClasses( collectingList, classesSearched );
				}
			}
		}
	}
	
	public List<TemplateClass> getCustomSetterListClasses()
	{
		List<TemplateClass> resultList = new ArrayList<TemplateClass>();
		List<TemplateClass> classesSearched = new ArrayList<TemplateClass>();
		collectSetterListClasses( resultList, classesSearched );
		return resultList;
	}
	
	private boolean alreadyContains(TemplateClass setterListType,
			List<TemplateClass> resultList)
	{
		for( TemplateClass clazz : resultList )
		{
			if( clazz.getInnerClass().equals( setterListType.getInnerClass() ))
			{
				return true;
			}
		}
		return false;
	}

	public String toString()
	{
		return "tmp: innerclass: " + clazz.getSimpleName();
	}

}
